# -*- coding: utf-8 -*-
import re
import logging
from datetime import datetime, timedelta

from sqlalchemy import func

from common import orm, slave
from common.account.model.account import Account
from common.coupon.model import (CouponTemplate, AccountCoupon, COUPON_STATUS, COUPON_TYPE, AwaitingCoupon, SOURCE_TYPE)
from common.transaction.db import create_sharding_transaction
from common.transaction.model import (TRANSACTION_TYPE, TRANSACTION_STATUS)
from common.utils import tracker
from common.utils.decorator import sql_wrapper, slave_wrapper
from common.utils.helper import list_object
from common.utils.tz import now_ts, get_utc_date, local_str_to_utc_str, \
    local_datetime_to_ts, utc_str_to_local_str, ts_to_local_datetime_str, local_to_utc_str

_LOGGER = logging.getLogger('lucky')


@sql_wrapper
def get_coupon(coupon_id, user_id, need_valid=True):
    query = AccountCoupon.query.filter(AccountCoupon.id == coupon_id) \
        .filter(AccountCoupon.user_id == user_id)
    if need_valid:
        now = now_ts()
        query = query.filter(AccountCoupon.status == COUPON_STATUS.UNUSED) \
            .filter(AccountCoupon.start_ts < now) \
            .filter(AccountCoupon.expire_ts > now)
    return query.first()


@slave_wrapper
def get_user_coupons(user_id, status, limit=0, offset=0):
    query = slave.session.query(AccountCoupon)
    count_query = slave.session.query(orm.func.count(AccountCoupon.id))
    query = query.filter(AccountCoupon.user_id == user_id)
    count_query = count_query.filter(AccountCoupon.user_id == user_id)
    if status is not None:
        junction = orm.or_
        status_filters = []
        if status & COUPON_STATUS.UNUSED:
            status_filters.append(AccountCoupon.status == COUPON_STATUS.UNUSED)
        if status & COUPON_STATUS.USED:
            status_filters.append(AccountCoupon.status == COUPON_STATUS.USED)
        if status & COUPON_STATUS.EXPIRED:
            status_filters.append(
                AccountCoupon.status == COUPON_STATUS.EXPIRED)
        query = query.filter(junction(*status_filters))
        count_query = count_query.filter(junction(*status_filters))
    count = count_query.all()[0][0]
    query = query.order_by(AccountCoupon.created_at.desc())
    if limit > 0:
        query = query.limit(limit)
    if offset > 0:
        query = query.offset(offset)
    coupons = query.all()
    return coupons, count


@sql_wrapper
def get_user_fresh_coupons(user_id):
    query = AccountCoupon.query.filter(AccountCoupon.user_id == user_id)
    coupons = query.filter(AccountCoupon.source_type == SOURCE_TYPE.REGISTER).all()
    return coupons


@sql_wrapper
def check_register_coupon(temp_id, user_id):
    item = AccountCoupon.query.filter(AccountCoupon.template_id == temp_id) \
        .filter(AccountCoupon.user_id == user_id).first()
    return True if item else False


@sql_wrapper
def list_coupon(query_dct):
    return list_object(query_dct, AccountCoupon)


@slave_wrapper
def get_coupon_stats(user_id):
    coupon_stats = {}
    query = slave.session.query(AccountCoupon)
    for status, key in COUPON_STATUS.to_dict().iteritems():
        coupon_stats[key] = {}
        query_res = query.filter(AccountCoupon.user_id == user_id) \
            .filter(AccountCoupon.status == status)
        if query_res.count():
            coupon_stats[key]['amount'] = query_res.with_entities(func.sum(AccountCoupon.price)).scalar()
        else:
            coupon_stats[key]['amount'] = 0
        coupon_stats[key]['total'] = query_res.count()

    return coupon_stats


@slave_wrapper
def get_unused_coupons(user_id, limit=0, offset=0):
    query = slave.session.query(AccountCoupon)
    query = query.filter(AccountCoupon.user_id == user_id) \
        .filter(AccountCoupon.status == COUPON_STATUS.UNUSED)
    query = query.order_by(AccountCoupon.start_ts).order_by(
        AccountCoupon.price.desc()).order_by(AccountCoupon.expire_ts)
    if limit > 0:
        query = query.limit(limit)
    if offset > 0:
        query = query.offset(offset)
    coupons = query.all()
    return coupons


@sql_wrapper
def get_coupon_by_id(coupon_id):
    return AccountCoupon.query.filter(AccountCoupon.id == coupon_id).first()


@sql_wrapper
def expire_coupon(coupon_id):
    coupon = AccountCoupon.query.filter(AccountCoupon.id == coupon_id).one()
    if coupon.status == COUPON_STATUS.UNUSED:
        coupon.status = COUPON_STATUS.EXPIRED
        coupon.save()
        log_data = coupon.as_dict()
        tracker.track_coupon(log_data, action='expire')


@sql_wrapper
def use_coupon(coupon):
    res = AccountCoupon.query.filter(AccountCoupon.id == coupon.id).filter(
        AccountCoupon.status == COUPON_STATUS.UNUSED).update({
        'status': COUPON_STATUS.USED,
        'updated_at': datetime.utcnow()
    })
    if res:
        # add balance
        account = Account.query.filter(
            Account.id == coupon.user_id).with_lockmode('update').first()
        account.balance += coupon.price
        account.save(auto_commit=False)
        # add transaction
        create_sharding_transaction(account.id, {'type': TRANSACTION_TYPE.AWARD,
                                                 'title': u'红包抵扣-%s' % coupon.title,
                                                 'price': coupon.price,
                                                 'balance': account.balance,
                                                 'status': TRANSACTION_STATUS.DONE,
                                                 })
        orm.session.commit()
        return True
    else:
        _LOGGER.warn(
            'use coupon fail, concurrency occurred!, coupon id[%s]' % coupon.id)
        return False


@sql_wrapper
def check_awaiting_coupon(user_id, phone):
    now = now_ts()
    items = AwaitingCoupon.query.filter(AwaitingCoupon.phone == phone) \
        .filter(AwaitingCoupon.expire_ts > now) \
        .filter(AwaitingCoupon.deleted == 0) \
        .all()
    log_datas = []
    for item in items:
        template_id = item.template_id
        template = CouponTemplate.query.filter(
            CouponTemplate.id == template_id).one()
        coupon = AccountCoupon.create_from_template(template, user_id)
        coupon.save(auto_commit=False)
        orm.session.flush()
        AccountCoupon.start_expire_timer(coupon.id, coupon.expire_ts)
        item.deleted = 1
        item.save(auto_commit=False)
        log_data = coupon.as_dict()
        log_data.update({
            'from': u'分享红包'
        })
        log_datas.append(log_data)
    orm.session.commit()
    for log_data in log_datas:
        tracker.track_coupon(log_data, action='create')


@slave_wrapper
def get_coupon_overview():
    today = get_utc_date()
    resp = {}
    resp["sent_count"], resp["sent_price"] = slave.session.query(
        func.count(AccountCoupon.id), func.sum(AccountCoupon.price)).filter(
        AccountCoupon.updated_at >= today).first()
    resp["used_count"], resp["used_price"] = slave.session.query(
        func.count(AccountCoupon.id), func.sum(AccountCoupon.price)).filter(
        AccountCoupon.status == COUPON_STATUS.USED).filter(
        AccountCoupon.updated_at >= today).first()
    resp["left_count"], resp["left_price"] = slave.session.query(
        func.count(AccountCoupon.id), func.sum(AccountCoupon.price)).filter(
        AccountCoupon.status == COUPON_STATUS.UNUSED).first()

    for k in resp:
        if resp[k] is None:
            resp[k] = 0

    return resp


@sql_wrapper
def award_coupon(user_id, tid):
    template = CouponTemplate.query.filter(CouponTemplate.id == tid).first()
    coupon = AccountCoupon.create_from_template(template, user_id)
    coupon.save()
    AccountCoupon.start_expire_timer(coupon.id, coupon.expire_ts)
    return coupon


def award_coupon_in_transaction(user_id, tid, start_ts=None, extend=None, coupon_from=None):
    template = CouponTemplate.query.filter(CouponTemplate.id == tid).first()
    coupon = AccountCoupon.create_from_template(
        template, user_id, extend=extend, start_ts=start_ts)
    coupon.save(auto_commit=False)
    orm.session.flush()
    AccountCoupon.start_expire_timer(coupon.id, coupon.expire_ts)
    log_data = coupon.as_dict()
    log_data.update({
        'from': coupon_from,
    })
    tracker.track_coupon(log_data, action='create')
    return coupon


@sql_wrapper
def upsert_coupon_template(query_dct):
    if 'id' in query_dct:
        t = CouponTemplate.query.with_for_update().filter(
            CouponTemplate.id == query_dct.pop('id')).first()
    else:
        t = CouponTemplate()

    for k, v in query_dct.iteritems():
        if hasattr(CouponTemplate, k) and k not in (
                'updated_at', 'created_at'):
            setattr(t, k, v)
    if t.coupon_type == COUPON_TYPE.DIRECT_OFF:
        t.condition_price = None
    t.save()


@sql_wrapper
def list_coupon_template(query_dct):
    return list_object(query_dct, CouponTemplate)


@sql_wrapper
def get_coupon_template(id):
    return CouponTemplate.query.filter(CouponTemplate.id == id).first()


@sql_wrapper
def get_all_coupon_templates():
    return CouponTemplate.query.all()


@sql_wrapper
def get_fresh_coupon_templates():
    return CouponTemplate.query.filter(CouponTemplate.source_type == SOURCE_TYPE.REGISTER).all()


def filter_coupon(user_id, coupon_type, status, source, effect_time, use_time, page, size):
    items = AccountCoupon.query.filter(AccountCoupon.user_id == user_id)\
            .order_by(AccountCoupon.start_ts.desc()).order_by(AccountCoupon.status)
    now_utc_datetime = datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S')
    last_year_utc_datetime = (datetime.utcnow() - timedelta(days=365)).strftime('%Y-%m-%d')
    if coupon_type:
        items = items.filter(AccountCoupon.coupon_type == coupon_type)
    if status:
        items = items.filter(AccountCoupon.status == status)
    if source == 'register':
        items = items.filter(AccountCoupon.source_type == SOURCE_TYPE.REGISTER)
    if source == 'system':
        items = items.filter(AccountCoupon.source_type != SOURCE_TYPE.REGISTER)
    if effect_time:
        effect_time = eval(effect_time)
        effect_start = local_datetime_to_ts(effect_time['$gte']) if effect_time.get('$gte') else 0
        effect_end = local_datetime_to_ts(effect_time['$lt']) if effect_time.get('$lt') else now_ts()
        items = items.filter(AccountCoupon.start_ts >= effect_start, AccountCoupon.start_ts < effect_end)
    if use_time:
        use_time = eval(use_time)
        use_start = local_str_to_utc_str(use_time['$gte']) if use_time.get('$gte') else last_year_utc_datetime
        use_end = local_str_to_utc_str(use_time['$lt']) if use_time.get('$lt') else now_utc_datetime
        items = items.filter(AccountCoupon.status == COUPON_STATUS.USED, AccountCoupon.created_at >= use_start,
                             AccountCoupon.created_at < use_end)
    total_count = items.count()
    unuse_count = items.filter(AccountCoupon.status == COUPON_STATUS.UNUSED, AccountCoupon.start_ts < now_ts(),
                               AccountCoupon.expire_ts > now_ts()).count()
    unuse_price = items.filter(AccountCoupon.status == COUPON_STATUS.UNUSED, AccountCoupon.start_ts < now_ts(),
                               AccountCoupon.expire_ts > now_ts()).with_entities(func.sum(AccountCoupon.price)).scalar() or 0
    used_count = items.filter(AccountCoupon.status == COUPON_STATUS.USED).count()
    used_price = items.filter(AccountCoupon.status == COUPON_STATUS.USED).with_entities(func.sum(AccountCoupon.price)).scalar() or 0
    expire_count = items.filter(AccountCoupon.status == COUPON_STATUS.EXPIRED).count()
    expire_price = items.filter(AccountCoupon.status == COUPON_STATUS.EXPIRED).with_entities(func.sum(AccountCoupon.price)).scalar() or 0
    items = items.limit(size).offset(size * (page - 1))
    data_list = coupons_handler(items)

    return {'data': data_list, 'total_count': total_count, 'page': page, 'size': len(data_list),
            'unuse_count': unuse_count, 'unuse_price': unuse_price, 'used_count': used_count,
            'used_price': used_price, 'expire_count': expire_count, 'expire_price': expire_price}


def coupons_handler(items):
    data_list = []
    for item in items:
        item = item.as_dict()
        if item['status'] == COUPON_STATUS.USED:
            item['use_time'] = utc_str_to_local_str('{}'.format(item['created_at']))
        item['created_at'] = utc_str_to_local_str('{}'.format(item['created_at']))
        item['updated_at'] = utc_str_to_local_str('{}'.format(item['updated_at']))
        item['start_ts'] = ts_to_local_datetime_str(item['start_ts'])
        item['expire_ts'] = ts_to_local_datetime_str(item['expire_ts'])
        if item['source_type'] == SOURCE_TYPE.REGISTER:
            item['source'] = 'register'
        else:
            item['source'] = 'system'
        data_list.append(item)

    return data_list
